'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
exports.syscall = exports.getGlobal = exports.USyscalls = exports.SyscallResponse = exports.ApiError = exports.ErrorCode = exports.SOCK = exports.AF = void 0;
var AF;
(function (AF) {
    AF[AF["UNSPEC"] = 0] = "UNSPEC";
    AF[AF["LOCAL"] = 1] = "LOCAL";
    AF[AF["UNIX"] = 1] = "UNIX";
    AF[AF["FILE"] = 1] = "FILE";
    AF[AF["INET"] = 2] = "INET";
    AF[AF["INET6"] = 10] = "INET6";
})(AF || (exports.AF = AF = {}));
var SOCK;
(function (SOCK) {
    SOCK[SOCK["STREAM"] = 1] = "STREAM";
    SOCK[SOCK["DGRAM"] = 2] = "DGRAM";
})(SOCK || (exports.SOCK = SOCK = {}));
// from BrowserFS.  Copied to avoid this module pulling in any dependencies
var ErrorCode;
(function (ErrorCode) {
    ErrorCode[ErrorCode["EPERM"] = 0] = "EPERM";
    ErrorCode[ErrorCode["ENOENT"] = 1] = "ENOENT";
    ErrorCode[ErrorCode["EIO"] = 2] = "EIO";
    ErrorCode[ErrorCode["EBADF"] = 3] = "EBADF";
    ErrorCode[ErrorCode["EACCES"] = 4] = "EACCES";
    ErrorCode[ErrorCode["EBUSY"] = 5] = "EBUSY";
    ErrorCode[ErrorCode["EEXIST"] = 6] = "EEXIST";
    ErrorCode[ErrorCode["ENOTDIR"] = 7] = "ENOTDIR";
    ErrorCode[ErrorCode["EISDIR"] = 8] = "EISDIR";
    ErrorCode[ErrorCode["EINVAL"] = 9] = "EINVAL";
    ErrorCode[ErrorCode["EFBIG"] = 10] = "EFBIG";
    ErrorCode[ErrorCode["ENOSPC"] = 11] = "ENOSPC";
    ErrorCode[ErrorCode["EROFS"] = 12] = "EROFS";
    ErrorCode[ErrorCode["ENOTEMPTY"] = 13] = "ENOTEMPTY";
    ErrorCode[ErrorCode["ENOTSUP"] = 14] = "ENOTSUP";
})(ErrorCode || (exports.ErrorCode = ErrorCode = {}));
// from BrowserFS.  Copied to avoid this module pulling in any dependencies
var fsErrors = {
    EPERM: 'Operation not permitted.',
    ENOENT: 'No such file or directory.',
    EIO: 'Input/output error.',
    EBADF: 'Bad file descriptor.',
    EACCES: 'Permission denied.',
    EBUSY: 'Resource busy or locked.',
    EEXIST: 'File exists.',
    ENOTDIR: 'File is not a directory.',
    EISDIR: 'File is a directory.',
    EINVAL: 'Invalid argument.',
    EFBIG: 'File is too big.',
    ENOSPC: 'No space left on disk.',
    EROFS: 'Cannot modify a read-only file system.',
    ENOTEMPTY: 'Directory is not empty.',
    ENOTSUP: 'Operation is not supported.',
};
// from BrowserFS.  Copied to avoid this module pulling in any dependencies
var ApiError = /** @class */ (function () {
    /**
     * Represents a BrowserFS error. Passed back to applications after a failed
     * call to the BrowserFS API.
     *
     * Error codes mirror those returned by regular Unix file operations, which is
     * what Node returns.
     * @constructor ApiError
     * @param type The type of the error.
     * @param [message] A descriptive error message.
     */
    function ApiError(type, message) {
        this.type = type;
        this.code = ErrorCode[type];
        if (message != null) {
            this.message = message;
        }
        else {
            this.message = fsErrors[type];
        }
    }
    ApiError.prototype.toString = function () {
        return this.code + ": " + fsErrors[this.code] + " " + this.message;
    };
    return ApiError;
}());
exports.ApiError = ApiError;
function convertApiErrors(e) {
    if (!e)
        return e;
    // if it looks like an ApiError, and smells like an ApiError...
    if (!e.hasOwnProperty('type') || !e.hasOwnProperty('message') || !e.hasOwnProperty('code'))
        return e;
    return new ApiError(e.type, e.message);
}
var SyscallResponse = /** @class */ (function () {
    function SyscallResponse(id, name, args) {
        this.id = id;
        this.name = name;
        this.args = args;
    }
    SyscallResponse.From = function (ev) {
        if (!ev.data)
            // @ts-ignore
            return;
        for (var i = 0; i < SyscallResponse.requiredOnData.length; i++) {
            if (!ev.data.hasOwnProperty(SyscallResponse.requiredOnData[i]))
                // @ts-ignore
                return;
        }
        var args = ev.data.args.map(convertApiErrors);
        return new SyscallResponse(ev.data.id, ev.data.name, args);
    };
    SyscallResponse.requiredOnData = ['id', 'name', 'args'];
    return SyscallResponse;
}());
exports.SyscallResponse = SyscallResponse;
var USyscalls = /** @class */ (function () {
    function USyscalls(port) {
        this.msgIdSeq = 1;
        this.outstanding = {};
        this.signalHandlers = {};
        this.port = port;
        this.port.onmessage = this.resultHandler.bind(this);
    }
    USyscalls.prototype.exit = function (code) {
        var msgId = this.nextMsgId();
        this.outstanding[msgId] = function () {
            var args = [];
            for (var _i = 0; _i < arguments.length; _i++) {
                args[_i] = arguments[_i];
            }
            console.log('received callback for exit(), should clean up');
        };
        this.post(msgId, 'exit', code);
    };
    USyscalls.prototype.fork = function (heap, cb) {
        var msgId = this.nextMsgId();
        this.outstanding[msgId] = cb;
        this.post(msgId, 'fork', heap);
    };
    USyscalls.prototype.kill = function (pid, sig, cb) {
        var msgId = this.nextMsgId();
        this.outstanding[msgId] = cb;
        this.post(msgId, 'kill', pid, sig);
    };
    USyscalls.prototype.wait4 = function (pid, options, cb) {
        var msgId = this.nextMsgId();
        this.outstanding[msgId] = cb;
        this.post(msgId, 'wait4', pid, options);
    };
    USyscalls.prototype.socket = function (domain, type, protocol, cb) {
        var msgId = this.nextMsgId();
        this.outstanding[msgId] = cb;
        this.post(msgId, 'socket', domain, type, protocol);
    };
    USyscalls.prototype.getsockname = function (fd, cb) {
        var msgId = this.nextMsgId();
        this.outstanding[msgId] = cb;
        this.post(msgId, 'getsockname', fd);
    };
    USyscalls.prototype.getpeername = function (fd, cb) {
        var msgId = this.nextMsgId();
        this.outstanding[msgId] = cb;
        this.post(msgId, 'getpeername', fd);
    };
    USyscalls.prototype.bind = function (fd, sockInfo, cb) {
        var msgId = this.nextMsgId();
        this.outstanding[msgId] = cb;
        this.post(msgId, 'bind', fd, sockInfo);
    };
    USyscalls.prototype.listen = function (fd, backlog, cb) {
        var msgId = this.nextMsgId();
        this.outstanding[msgId] = cb;
        this.post(msgId, 'listen', fd, backlog);
    };
    USyscalls.prototype.accept = function (fd, cb) {
        var msgId = this.nextMsgId();
        this.outstanding[msgId] = cb;
        this.post(msgId, 'accept', fd);
    };
    USyscalls.prototype.connect = function (fd, addr, cb) {
        var msgId = this.nextMsgId();
        this.outstanding[msgId] = cb;
        this.post(msgId, 'connect', fd, addr);
    };
    USyscalls.prototype.getcwd = function (cb) {
        var msgId = this.nextMsgId();
        this.outstanding[msgId] = cb;
        this.post(msgId, 'getcwd');
    };
    USyscalls.prototype.getpid = function (cb) {
        var msgId = this.nextMsgId();
        this.outstanding[msgId] = cb;
        this.post(msgId, 'getpid');
    };
    USyscalls.prototype.getppid = function (cb) {
        var msgId = this.nextMsgId();
        this.outstanding[msgId] = cb;
        this.post(msgId, 'getppid');
    };
    USyscalls.prototype.spawn = function (cwd, name, args, env, files, cb) {
        var msgId = this.nextMsgId();
        this.outstanding[msgId] = cb;
        this.post(msgId, 'spawn', cwd, name, args, env, files);
    };
    USyscalls.prototype.pipe2 = function (flags, cb) {
        var msgId = this.nextMsgId();
        this.outstanding[msgId] = cb;
        this.post(msgId, 'pipe2', flags);
    };
    USyscalls.prototype.getpriority = function (which, who, cb) {
        var msgId = this.nextMsgId();
        this.outstanding[msgId] = cb;
        this.post(msgId, 'getpriority', which, who);
    };
    USyscalls.prototype.setpriority = function (which, who, prio, cb) {
        var msgId = this.nextMsgId();
        this.outstanding[msgId] = cb;
        this.post(msgId, 'setpriority', which, who, prio);
    };
    USyscalls.prototype.open = function (path, flags, mode, cb) {
        var msgId = this.nextMsgId();
        this.outstanding[msgId] = cb;
        this.post(msgId, 'open', path, flags, mode);
    };
    USyscalls.prototype.unlink = function (path, cb) {
        var msgId = this.nextMsgId();
        this.outstanding[msgId] = cb;
        this.post(msgId, 'unlink', path);
    };
    USyscalls.prototype.utimes = function (path, atime, mtime, cb) {
        var msgId = this.nextMsgId();
        this.outstanding[msgId] = cb;
        this.post(msgId, 'utimes', path, atime, mtime);
    };
    USyscalls.prototype.futimes = function (fd, atime, mtime, cb) {
        var msgId = this.nextMsgId();
        this.outstanding[msgId] = cb;
        this.post(msgId, 'futimes', fd, atime, mtime);
    };
    USyscalls.prototype.rmdir = function (path, cb) {
        var msgId = this.nextMsgId();
        this.outstanding[msgId] = cb;
        this.post(msgId, 'rmdir', path);
    };
    USyscalls.prototype.mkdir = function (path, mode, cb) {
        var msgId = this.nextMsgId();
        this.outstanding[msgId] = cb;
        this.post(msgId, 'mkdir', path);
    };
    USyscalls.prototype.close = function (fd, cb) {
        var msgId = this.nextMsgId();
        this.outstanding[msgId] = cb;
        this.post(msgId, 'close', fd);
    };
    USyscalls.prototype.pwrite = function (fd, buf, pos, cb) {
        var msgId = this.nextMsgId();
        this.outstanding[msgId] = cb;
        this.post(msgId, 'pwrite', fd, buf, pos);
    };
    USyscalls.prototype.readdir = function (path, cb) {
        var msgId = this.nextMsgId();
        this.outstanding[msgId] = cb;
        this.post(msgId, 'readdir', path);
    };
    USyscalls.prototype.fstat = function (fd, cb) {
        var msgId = this.nextMsgId();
        this.outstanding[msgId] = cb;
        this.post(msgId, 'fstat', fd);
    };
    USyscalls.prototype.lstat = function (path, cb) {
        var msgId = this.nextMsgId();
        this.outstanding[msgId] = cb;
        this.post(msgId, 'lstat', path);
    };
    USyscalls.prototype.chdir = function (path, cb) {
        var msgId = this.nextMsgId();
        this.outstanding[msgId] = cb;
        this.post(msgId, 'chdir', path);
    };
    USyscalls.prototype.stat = function (path, cb) {
        var msgId = this.nextMsgId();
        this.outstanding[msgId] = cb;
        this.post(msgId, 'stat', path);
    };
    USyscalls.prototype.ioctl = function (fd, request, length, cb) {
        var msgId = this.nextMsgId();
        this.outstanding[msgId] = cb;
        this.post(msgId, 'ioctl', fd, request, length);
    };
    USyscalls.prototype.readlink = function (path, cb) {
        var msgId = this.nextMsgId();
        this.outstanding[msgId] = cb;
        this.post(msgId, 'readlink', path);
    };
    USyscalls.prototype.getdents = function (fd, length, cb) {
        var msgId = this.nextMsgId();
        this.outstanding[msgId] = cb;
        this.post(msgId, 'getdents', fd, length);
    };
    USyscalls.prototype.pread = function (fd, length, offset, cb) {
        var msgId = this.nextMsgId();
        this.outstanding[msgId] = cb;
        this.post(msgId, 'pread', fd, length, offset);
    };
    USyscalls.prototype.addEventListener = function (type, handler) {
        if (!handler)
            return;
        if (this.signalHandlers[type])
            this.signalHandlers[type].push(handler);
        else
            this.signalHandlers[type] = [handler];
    };
    USyscalls.prototype.resultHandler = function (ev) {
        var response = SyscallResponse.From(ev);
        if (!response) {
            console.log('bad usyscall message, dropping');
            console.log(ev);
            return;
        }
        // signals are named, everything else is a response
        // to a message _we_ sent.  Signals include the
        // 'init' message with our args + environment.
        if (response.name) {
            var handlers = this.signalHandlers[response.name];
            if (handlers) {
                for (var i = 0; i < handlers.length; i++)
                    handlers[i](response);
            }
            else {
                console.log('unhandled signal ' + response.name);
            }
            return;
        }
        // TODO: handle reject
        this.complete(response.id, response.args);
    };
    USyscalls.prototype.complete = function (id, args) {
        var cb = this.outstanding[id];
        delete this.outstanding[id];
        if (cb) {
            cb.apply(undefined, args);
        }
        else {
            console.log('unknown callback for msg ' + id + ' - ' + args);
        }
    };
    USyscalls.prototype.nextMsgId = function () {
        return ++this.msgIdSeq;
    };
    USyscalls.prototype.post = function (msgId, name) {
        var args = [];
        for (var _i = 2; _i < arguments.length; _i++) {
            args[_i - 2] = arguments[_i];
        }
        this.port.postMessage({
            id: msgId,
            name: name,
            args: args,
        });
    };
    return USyscalls;
}());
exports.USyscalls = USyscalls;
function getGlobal() {
    // logic from gopherjs
    if (typeof window !== "undefined") { /* web page */
        return window;
    }
    else if (typeof self !== "undefined") { /* web worker */
        return self;
    }
    else if (typeof global !== "undefined") { /* Node.js */
        return global;
    }
    else { /* others (e.g. Nashorn) */
        return this;
    }
}
exports.getGlobal = getGlobal;
exports.syscall = new USyscalls(getGlobal());
